﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using eBookDirectory.Infrastructure.Entities;

namespace eBookDirectory.Infrastructure.Repository
{
    public class RepositoryEntityStore<TEntity>
        : IRepository<TEntity>
    {
        protected readonly IDictionary<long, TEntity> RepositoryMap = new Dictionary<long, TEntity>();

        public TEntity Save(TEntity instance)
        {
            var entity = GetEntityInstance(instance);
            if (entity.Id != 0)
            {
                throw new ApplicationException("Entity instance cannot be saved, the Id field was not cero");
            }

            GetNewId(instance);
            RepositoryMap.Add(entity.Id, instance);
            return instance;
        }

        public void Update(TEntity instance)
        {
            var entity = GetEntityInstance(instance);
            if (entity.Id == 0)
            {
                throw new ApplicationException("Entity instance cannot be updated, the Id field was cero");
            }
            RepositoryMap[entity.Id] = instance;
        }

        public void Remove(TEntity instance)
        {
            IEntity entityInstance = GetEntityInstance(instance);
            RepositoryMap.Remove(entityInstance.Id);
        }

        /// <summary>
        /// Ermittelt die höchste ID aus dem RepositoryMap
        /// </summary>
        /// <param name="instance">In diesem DTO wird die neue ID gesetzt</param>
        private void GetNewId(TEntity instance)
        {
            long max = 0;
            if (RepositoryMap.Count > 0)
            {
                max = RepositoryMap.Keys.Max();
            }

            max = ++max;
            var setter = GetSetter(instance.GetType());
            setter.Invoke(instance, new object[] { max });
        }

        private readonly IDictionary<Type, MethodInfo> Setters = new Dictionary<Type, MethodInfo>();

        private MethodInfo GetSetter(Type type)
        {
            if (Setters.ContainsKey(type))
                return Setters[type];

            var setter = type.GetProperty("Id").GetSetMethod(true);
            Setters.Add(type, setter);
            return setter;
        }

        private IEntity GetEntityInstance(TEntity instance)
        {
            var entityInstance = instance as IEntity;
            if (entityInstance == null)
                throw new ArgumentException("Passed instance is not an IEntity");
            return entityInstance;
        }



        public TEntity GetById(long id)
        {
            return RepositoryMap[id];
        }

        public IQueryable<TEntity> FindAll()
        {
            return RepositoryMap.Values.AsQueryable();
        }
    }
}